1.编码
URL编码
URL编码的目的是把任意文本数据编码为%前缀表示的文本,便于浏览器和服务器处理.
如果字符是AZ,az,0~9以及-、_、.、*,则保持不变
如果是其他字符,先转换为UTF-8编码,然后对每个字节以%XX表示
1 | // URLEncoder是java.net包的 |
Base64编码
Base64编码的目的是把任意二进制数据编码为文本,但编码后数据量会增加1/3.
例如,电子邮件协议就是文本协议,如果要在电子邮件中添加一个二进制文件,就可以用Base64编码,然后以文本的形式传送.
Base64编码将二进制数据每3个字节编码成4个字符,如果二进制数据字节数不是3的倍数,会在末尾添加0x00进行补充.
例如:
二进制:e4 b8 ad
二进制的3个字节的数据一共有3 * 8 = 24bit,在使用Base64编码的时候,每6bit编码成一个字符,即24 / 6 = 4个字符.
e4 b8 ad 编码成Base64的结果就是 5Lit
二进制:e4 b8 ad 21
二进制的4个字节的数据一共有4 * 8 = 32bit,在使用Base64编码的时候,每6bit编码成一个字符,即32 / 6 不是一个整数,那就需要在末尾补充0x00,由于Base64编码是每3个字节为单位进行编码的,意味着二进制的数据e4 b8 ad 21需要添加两个0x00凑成6个字节(3的倍数)进行编码,
e4 b8 ad 21 00 00 编码成Base64的结果就是 5LitIQ==, 每一个=
代表一个0x00
实际上,在二进制数据编码成Base64过程中在末尾补充的0x00,在Base64解码时是先去掉=
的,也就是说即使不添加0x00,也是能正常解码的.
可以使用withoutPadding()在编码时不添加0x00
1 | byte[] input = new byte[]{(byte)0xe4,(byte)0xb8,(byte)0xad,(byte)0x21}; |
另外还有一点,Base64编码后的字符加上=
的长度一定会是4的倍数.
因为标准的Base64编码会出现+、/和=,所以不适合把Base64编码后的字符串放到URL中.一种针对URL的Base64编码可以在URL中使用的Base64编码,它仅仅是把+
变成-
,/
变成_
1 | String encode2 = Base64.getUrlEncoder().encodeToString(input); |
2.哈希算法
HMAC
1 | public static void hmacMD5_encode() throws NoSuchAlgorithmException, InvalidKeyException { |
对称加密
AES
AES生成128的密文,需要的密钥一定是16bit.
工作模式:ECB
ECB模式下,固定的密钥和明文,每次加密生成的密文都是一样的.
1 | public static String aesDecrypt(String key,byte[] input) throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException, NoSuchPaddingException, NoSuchAlgorithmException { |
工作模式:CBC
CBC模式下,固定的密钥和明文,每次加密生成的密文是不一样的.
1 | public static void main(String[] args) throws Exception { |
出现错误:
1 | javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption. |
这是由于SecureRandom的生成方式不同而导致的错误,可能在window系统上可以运行,在linux系统上不能运行.
1 | public static String aesDecrypt(String key,byte[] input) throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException, NoSuchPaddingException, NoSuchAlgorithmException { |
关于AES的加密方式:
方式1: 密钥只能是16bit,否则报错
1 | java.security.InvalidKeyException: Invalid AES key length: 9 bytes |
1 | Cipher cipher = Cipher.getInstance("AES"); |
方式2:密钥不一定要16bit
使用SecretKey.
1 | Cipher cipher = Cipher.getInstance("AES"); |
或者使用SecretKeySpec也可以.
1 | Cipher cipher = Cipher.getInstance("AES"); |
密钥交换算法
DH
DH算法不传递密钥,而是使用对方的公钥生成密钥.
1 | public class DHTest { |
非对称加密
RSA
1 | public class RSATest { |
从byte[]恢复公钥和私钥的方法:
1 | byte[] pkData = ... |
数字签名
数字签名
的本质就是用发送方的私钥进行签名.
一般来说,签名的时候并不是直接对原始数据签名,而是对原始数据生成哈希摘要(如果使用RSA算法的话,直接对原始数据进行签名,对原始数据的长度有限制.只有将任意长度的原始消息转换成固定长度的哈希摘要才能保证加密成功),再使用私钥对哈希摘要进行签名.
签名验证
的本质就是用发送方的公钥进行解密
用发送方的公钥进行解密,如果能正确解密,说明数据一定是发送方发送的,因为能够使用发送方的公钥正确解密,那一定是拥有发送方私钥加密的,保证了发送方的抗否认性
.同时,获取到发送方生成的哈希摘要.再使用同样的哈希方法对发送方发送的原始数据计算出哈希摘要,自己计算的哈希摘要与发送方生成的哈希摘要进行对比,如果摘要一致,说明数据没有被篡改,保证了数据的完整性
.
由此可见,私钥就相当于用户身份,公钥是用来给外部验证用户身份的.数字签名可以确保数据完整性
和抗否认性
.数字签名可以用于:
1.防止伪造|抵赖:如果签名验证成功了,说明数据一定是某一个拥有私钥的发送方发送的,任何人无法伪造,发送方也无法抵赖.
2.检测篡改: 如果签名验证后获取的摘要与自己重新计算的摘要一致,说明数据没有被篡改.
数字签名存在的问题:
1.无法验证发送方的身份
.比如,黑客进行了DNS挟持
,举个例子,我们使用浏览器访问baidu.com,原本DNS服务器是将baidu.com解析成百度对应的服务器地址的.但是黑客入侵网关修改了DNS服务器,把原本正确的服务器地址修改成了黑客自己的服务器地址,并且黑客伪造了自己的私钥和公钥,当你用浏览器访问时,黑客的服务器会返回给你公钥,浏览器进行签名验证,签名验证依然通过,但这个公钥已经不是baidu.com的公钥了,而是黑客伪造的公钥,以后黑客就可以伪造成baidu.com与浏览器通信.(数字证书
可以解决这个问题,因为数字证书引入了CA认证
,由CA去验证发送方的身份).
常用的签名算法有:
1.MD5withRSA
2.SHA1withRSA
3.SHA256withRSA
1 | public class Main { |
数字证书
数字签名
能保证数据的完整性和抗否定性.但是却不能验证公钥的身份.而数字证书的出现,就是为了证明公钥持有者的身份
.
一般会在证书中包含以下几类信息:
1.公钥信息
2.拥有者身份信息
3.CA信息,有效时间,证书序列号等
4.CA对该证书的数字签名
证书拥有者通过该证书,即可以向系统或者其他用户证明身份,从而获得对方信任并授权使用某些敏感服务.
证书的本质就是CA用自己的私钥对公钥持有者的公钥进行数字签名
.其他用户只要用CA的公钥去验证证书上的数字签名,就可以证明公钥持有者的身份.
数字证书能避免中间人攻击
.
HTTPS
在将数据提交给HTTP层之后,数据会经过用户电脑,wifi路由器,运营商和目标服务器,在这中间的每个环节中,数据都有可能被窃取或篡改.比如用户电脑被黑客装了恶意软件.HTTP传输的内容很容易被中间人窃取|伪造|篡改,通常我们把这种攻击方式称为中间人攻击
.
我们可以在HTTP和TCP之间插入一个安全层,所有经过安全层的数据都会被加密或者解密.
过程:
1.浏览器请求服务器数据
2.服务器用自己的私钥加密网页内容后,连同数字证书一起发送给浏览器
3.数字证书是经过CA的私钥加密的,浏览器用操作系统自带的Root CA来验证服务器的证书是否有效(浏览器查看证书管理器
的受信任的根证书颁发机构
列表中是否存在解开数字证书的公钥?)
可能会发生3种情况:
a.如果数字证书记载的网站与当前访问的网站不一样,浏览器会发出警告
b.如果数字证书不是受信任的机构办法的,浏览器会发出另外一种警告
c.如果数字证书是可靠的,浏览器就可以使用数字证书中携带的服务器的公钥
加密一个随机的AES密钥发送给服务器,服务器会用自己的私钥解密获得AES密钥,在随后的通讯中使用AES对称加密(因为AES对称加密解密的速度快)